package com.xisoft.common.pubEnv;


import com.xisoft.common.constant.IPubEvnCons;
import com.xisoft.common.math.MathUtil;

import java.util.Collections;
import java.util.HashMap;
import java.util.Stack;

/**
 * 算数表达式求值
 * 直接调用Calculator的类方法conversion()
 * 传入算数表达式，将返回一个浮点值结果
 * 如果计算过程错误，将返回一个NaN
 *
 * @fileRelativePath: CalculatorHelper.java
 * @Date: Created on 2021/9/18  13:48  <br>
 * @author: LiBencheng<br>
 * @version: 1.0<br>
 */
public class CalculatorHelper
{
    /** 后缀式栈 */
    private Stack< String > postfixStack = new Stack< String >();
    /** 运算符栈 */
    private Stack< Character > opStack = new Stack< Character >();
    /** 运用运算符ASCII码-40做索引的运算符优先级 */
    private int[] operatPriority = new int[]{0, 3, 2, 1, -1, 1, 0, 2};

    public static double convOfZero(String expression)
    {
        double value = CalculatorHelper.conversion(expression);
        return Double.valueOf(value).isNaN() ? 0.0 : value;
    }

    public static String convStrOfZero(String expression)
    {
        String str = conversionStr(expression);
        return Double.valueOf(str).isNaN() ? PubEnvUtil.ZERO : str;
    }

    public static double conversion(String expression)
    {
        double result = 0;
        CalculatorHelper cal = new CalculatorHelper();
        try
        {
            leftIndexMap = new HashMap< Integer,Integer >();
            leftToRightMap = new HashMap< Integer,Integer >();
            // expression = transform(expression);
            result = cal.calculate(expression);
        }
        catch (Exception e)
        {
            // e.printStackTrace();
            // 运算错误返回NaN
            LoggerUtil.getInstance("CalculatorLogs")
                      .exception("CalculatorHelper.conversion：" + expression + "【运算错误返回NaN】，" + e.getMessage(), e);
            return 0.0 / 0.0;
        }
        // return new String().valueOf(result);
        return result;
    }

    public static String conversionStr(String expression)
    {
        String result = "0";
        CalculatorHelper cal = new CalculatorHelper();
        try
        {
            leftIndexMap = new HashMap< Integer,Integer >();
            leftToRightMap = new HashMap< Integer,Integer >();
            // expression = transform(expression);
            result = cal.calculateStr(expression);
        }
        catch (Exception e)
        {
            // 运算错误返回NaN
            LoggerUtil.getInstance("CalculatorLogs")
                      .exception("CalculatorHelper.conversionStr：" + expression + "【运算错误返回NaN】，" + e.getMessage(), e);
            return "NaN";
        }
        // return new String().valueOf(result);
        return result;
    }

    /************************************************************* <br>
     *
     *说明: 将表达式中负数的符号更改<br>
     *
     * @Description   <br>
     * @Date Created on 2021/9/18  13:48  <br>
     * @Param [expression 例如-2+-1*(-3E-2)-(-1) 被转为 ~2+~1*(~3E~2)-(~1) ] <br>
     * @return java.lang.String  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    private static String transform(String expression)
    {
        char[] arr = expression.toCharArray();
        for (int i = 0; i < arr.length; i++)
        {
            if (arr[i] == '-')
            {
                if (i == 0)
                { arr[i] = '~'; }
                else
                {
                    char c = arr[i - 1];
                    if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == 'E' || c == 'e')
                    { arr[i] = '~'; }
                }
            }
        }
        //add  by  mizhl  2022-07-25
        if (arr.length > 1)
        {
            if (arr[0] == '~' || arr[1] == '(')
            {
                arr[0] = '-';
                return "0" + new String(arr);
            }
            else { return new String(arr); }
        }
        else
        {
            return new String(arr);
        }
    }

    /** 左边括号的下标 */
    static HashMap< Integer,Integer > leftIndexMap = null;
    /** 对应右边括号的下标 */
    static HashMap< Integer,Integer > leftToRightMap = null;

    /************************************************************* <br>
     *
     *说明: 处理括号，括号里面的先计算<br>
     *
     * @Description   <br>
     * @Date Created on 2021/11/26  20:38  <br>
     * @param formulaStr <br>
     * @return java.lang.String  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    private String parseBrace(String formulaStr)
    {
        int large_Left = 0, large_Right = 0;
        char[] chars = formulaStr.toCharArray();
        int left_num = -1, right_num = -1;
        HashMap< Integer,Integer > indexMap = new HashMap< Integer,Integer >();
        for (int i = 0; i < chars.length; i++)
        {
            char aChar = chars[i];
            if (aChar == '(')
            {
                large_Left++;
                left_num = i;
                indexMap.put(large_Left, left_num);
                leftIndexMap.put(large_Left, left_num);
            }
            if (aChar == ')')
            {
                large_Right++;
                right_num = i;
            }
            if (large_Left != 0 && large_Right != 0)
            {
                if (large_Left > large_Right) { left_num = indexMap.get(large_Left); }
                leftToRightMap.put(left_num, right_num);
                if (formulaStr.contains(IPubEvnCons.LEFT_BRACKET) && formulaStr.contains(IPubEvnCons.RIGHT_BRACKET))
                {
                    formulaStr = localCalc(formulaStr, left_num, right_num);
                    formulaStr = parseBrace(formulaStr);
                    large_Left = -1;
                    large_Right = 0;
                    left_num = -1;
                    right_num = -1;
                    indexMap.clear();
                }
            }
        }

        return formulaStr;
    }

    /************************************************************* <br>
     *
     *说明: 运算表达式，先计算括号里面的内容，在拼接前后内容<br>
     *
     * @Description   <br>
     * @Date Created on 2021/11/26  20:38  <br>
     * @param formulaStr
     * @param leftIndex
     * @param rightIndex <br>
     * @return java.lang.String  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public String localCalc(String formulaStr, int leftIndex, int rightIndex)
    {
        String labelStr = "";
        //头部
        String startStr = formulaStr.substring(0, leftIndex);
        //中部middle
        String midStr = formulaStr.substring(leftIndex + 1, rightIndex);
        //尾部
        String subEndStr = formulaStr.substring(rightIndex + 1, formulaStr.length());
        if (PubEnvUtil.isEmpty(midStr)) { midStr = PubEnvUtil.ZERO; }

        labelStr = subLabel(startStr, IPubEvnCons.EMPTY) + exeCalculate(midStr) + subEndStr;
        // labelStr = subLabel(startStr, IPubEvnCons.EMPTY) + exeCalculate(transform(midStr)) + subEndStr;

        return labelStr;
    }

    public String subLabel(String subStr, String splitStr)
    { return subStr.substring(0, subStr.length() - splitStr.length()); }

    /************************************************************* <br>
     *
     *说明: <br>
     *
     * @Description   <br>
     * @Date Created on 2021/11/26  20:48  <br>
     * @param calcStr <br>
     * @return double  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public double calculate(String calcStr)
    { return Double.parseDouble(calculateStr(parseBrace(calcStr))); }

    public String calculateStr(String expression)
    { return exeCalculate(expression); }

    /************************************************************* <br>
     *
     *说明:按照给定的表达式计算 <br>
     *
     * @Description   <br>
     * @Date Created on 2021/9/18  13:49  <br>
     * @Param [expression 要计算的表达式例如:5+12*(3+5)/7 ] <br>
     * @return double  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    private String exeCalculate(String expression)
    {
        Stack< String > resultStack = new Stack< String >();
        // prepare(expression);
        prepare(transform(expression));
        // 将后缀式栈反转
        Collections.reverse(postfixStack);
        String firstValue, secondValue, currentValue;// 参与计算的第一个值，第二个值和算术运算符
        while (!postfixStack.isEmpty())
        {
            currentValue = postfixStack.pop();
            if (!isOperator(currentValue.charAt(0)))
            {// 如果不是运算符则存入操作数栈中
                currentValue = currentValue.replace("~", "-");
                resultStack.push(currentValue);
            }
            else
            {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算
                secondValue = resultStack.pop();
                firstValue = resultStack.pop();

                // 将负数标记符改为负号
                firstValue = firstValue.replace("~", "-");
                secondValue = secondValue.replace("~", "-");

                String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
                resultStack.push(tempResult);
            }
        }
        // String popStr = resultStack.pop();
        // return Double.p/arseDouble(popStr);
        // return new Long(popStr).doubleValue();
        return resultStack.pop();
    }


    /************************************************************* <br>
     *
     *说明: 数据准备阶段将表达式转换成为后缀式栈<br>
     *
     * @Description   <br>
     * @Date Created on 2021/9/18  13:49  <br>
     * @Param [expression] <br>
     * @return void  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    private void prepare(String expression)
    {
        // 运算符放入栈底元素逗号，此符号优先级最低
        opStack.push(',');
        char[] arr = expression.toCharArray();
        // 当前字符的位置
        int currentIndex = 0;
        // 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
        int count = 0;
        // 当前操作符和栈顶操作符
        char currentOp, peekOp;
        for (int i = 0; i < arr.length; i++)
        {
            currentOp = arr[i];
            if (isOperator(currentOp))
            {
                // 如果当前字符是运算符
                if (count > 0)
                {
                    // 取两个运算符之间的数字
                    postfixStack.push(new String(arr, currentIndex, count));
                }
                peekOp = opStack.peek();
                if (currentOp == ')')
                {
                    // 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
                    while (opStack.peek() != '(')
                    {
                        postfixStack.push(String.valueOf(opStack.pop()));
                    }
                    opStack.pop();
                }
                else
                {
                    while (currentOp != '(' && peekOp != ',' && compare(currentOp, peekOp))
                    {
                        postfixStack.push(String.valueOf(opStack.pop()));
                        peekOp = opStack.peek();
                    }
                    opStack.push(currentOp);
                }
                count = 0;
                currentIndex = i + 1;
            }
            else
            {
                count++;
            }
        }
        if (count > 1 || (count == 1 && !isOperator(arr[currentIndex])))
        {
            // 最后一个字符不是括号或者其他运算符的则加入后缀式栈中
            postfixStack.push(new String(arr, currentIndex, count));
        }

        while (opStack.peek() != ',')
        {
            postfixStack.push(String.valueOf(opStack.pop()));// 将操作符栈中的剩余的元素添加到后缀式栈中
        }
    }


    /************************************************************* <br>
     *
     *说明:判断是否为算术符号<br>
     *
     * @Description   <br>
     * @Date Created on 2021/9/18  13:51  <br>
     * @Param [c] <br>
     * @return boolean  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    private boolean isOperator(char c)
    { return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')'; }


    /************************************************************* <br>
     *
     *说明: 利用ASCII码-40做下标去算术符号优先级<br>
     *
     * @Description   <br>
     * @Date Created on 2021/9/18  13:51  <br>
     * @Param [cur, peek] <br>
     * @return boolean  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public boolean compare(char cur, char peek)
    {
        // 如果是peek优先级高于cur，返回true，默认都是peek优先级要低
        boolean result = false;
        if (operatPriority[(peek) - 40] >= operatPriority[(cur) - 40])
        {
            result = true;
        }
        return result;
    }


    /************************************************************* <br>
     *
     *说明:按照给定的算术运算符做计算 <br>
     *
     * @Description   <br>
     * @Date Created on 2021/9/18  13:51  <br>
     * @Param [firstValue, secondValue, currentOp] <br>
     * @return java.lang.String  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    private String calculate(String firstValue, String secondValue, char currentOp)
    {
        String result = "";
        switch (currentOp)
        {
            case '+':
                result = MathUtil.add(firstValue, secondValue);
                break;
            case '-':
                result = MathUtil.subtract(firstValue, secondValue);
                break;
            case '*':
                result = MathUtil.multiply(firstValue, secondValue);
                break;
            case '/':
                // 薪资计算，精度保留8位为宜
                result = MathUtil.divide(firstValue, secondValue, 8);
                break;
            default:
                result = "";
        }

        return result;
    }
}